import json
import logging
from typing import Optional, Any, Dict, List, Union

from redis.asyncio import Redis, ConnectionPool
from redis.exceptions import RedisError

from app.config import settings
from app.utils.logging import get_logger

# 创建日志对象
logger = get_logger(__name__)

class RedisClient:
    """
    Redis客户端类，提供异步Redis操作和连接池管理
    """
    
    def __init__(self):
        self._pool: Optional[ConnectionPool] = None
        self._redis: Optional[Redis] = None
        self._is_connected: bool = False
    
    async def init_redis(self) -> None:
        """初始化Redis连接池和客户端"""
        try:
            logger.info(f"Initializing Redis connection to {settings.redis.redis_host}:{settings.redis.redis_port}")
            
            # 创建连接池
            self._pool = ConnectionPool.from_url(
                settings.redis.redis_url,
                max_connections=settings.redis.redis_pool_size,
                decode_responses=True  # 自动解码响应
            )
            
            # 创建客户端
            self._redis = Redis(connection_pool=self._pool)
            
            # 测试连接
            await self._test_connection()
            
            logger.info("Redis connection initialized successfully")
        except Exception as e:
            logger.error(f"Failed to initialize Redis: {str(e)}")
            raise
    
    async def _test_connection(self) -> None:
        """测试Redis连接"""
        try:
            if await self._redis.ping():
                self._is_connected = True
                logger.info("Redis connection test successful")
            else:
                self._is_connected = False
                logger.error("Redis connection test failed: PING command returned False")
        except Exception as e:
            self._is_connected = False
            logger.error(f"Redis connection test failed: {str(e)}")
            raise
    
    async def close(self) -> None:
        """关闭Redis连接"""
        logger.info("Closing Redis connections")
        if self._redis:
            await self._redis.close()
            self._redis = None
        
        if self._pool:
            await self._pool.disconnect()
            self._pool = None
        
        self._is_connected = False
        logger.info("Redis connections closed")
    
    async def get(self, key: str) -> Optional[Any]:
        """获取缓存值"""
        try:
            if not self._redis or not self._is_connected:
                logger.warning("Redis not connected, attempting to initialize")
                await self.init_redis()
            
            value = await self._redis.get(key)
            if value:
                try:
                    # 尝试解析JSON
                    return json.loads(value)
                except json.JSONDecodeError:
                    # 如果不是JSON，返回原始值
                    return value
            return None
        except RedisError as e:
            logger.error(f"Redis get error for key '{key}': {str(e)}")
            return None
    
    async def set(self, key: str, value: Any, ttl: Optional[int] = None) -> bool:
        """设置缓存值"""
        try:
            if not self._redis or not self._is_connected:
                logger.warning("Redis not connected, attempting to initialize")
                await self.init_redis()
            
            # 如果值是字典或列表，将其序列化为JSON
            if isinstance(value, (dict, list)):
                value = json.dumps(value)
            
            # 设置值，如果有TTL则设置过期时间
            if ttl is not None:
                return await self._redis.set(key, value, ex=ttl)
            else:
                return await self._redis.set(key, value)
        except RedisError as e:
            logger.error(f"Redis set error for key '{key}': {str(e)}")
            return False
    
    async def delete(self, key: str) -> bool:
        """删除缓存值"""
        try:
            if not self._redis or not self._is_connected:
                logger.warning("Redis not connected, attempting to initialize")
                await self.init_redis()
            
            return bool(await self._redis.delete(key))
        except RedisError as e:
            logger.error(f"Redis delete error for key '{key}': {str(e)}")
            return False
    
    async def exists(self, key: str) -> bool:
        """检查缓存键是否存在"""
        try:
            if not self._redis or not self._is_connected:
                logger.warning("Redis not connected, attempting to initialize")
                await self.init_redis()
            
            return bool(await self._redis.exists(key))
        except RedisError as e:
            logger.error(f"Redis exists error for key '{key}': {str(e)}")
            return False
    
    async def clear_pattern(self, pattern: str) -> int:
        """清除匹配模式的所有缓存"""
        try:
            if not self._redis or not self._is_connected:
                logger.warning("Redis not connected, attempting to initialize")
                await self.init_redis()
            
            keys = await self._redis.keys(pattern)
            if not keys:
                return 0
            
            return await self._redis.delete(*keys)
        except RedisError as e:
            logger.error(f"Redis clear_pattern error for pattern '{pattern}': {str(e)}")
            return 0
    
    async def get_ttl(self, key: str) -> int:
        """获取键的过期时间（秒）"""
        try:
            if not self._redis or not self._is_connected:
                logger.warning("Redis not connected, attempting to initialize")
                await self.init_redis()
            
            return await self._redis.ttl(key)
        except RedisError as e:
            logger.error(f"Redis ttl error for key '{key}': {str(e)}")
            return -2  # -2表示键不存在
    
    async def set_nx(self, key: str, value: Any, ttl: Optional[int] = None) -> bool:
        """只在键不存在时设置值（用于分布式锁）"""
        try:
            if not self._redis or not self._is_connected:
                logger.warning("Redis not connected, attempting to initialize")
                await self.init_redis()
            
            # 如果值是字典或列表，将其序列化为JSON
            if isinstance(value, (dict, list)):
                value = json.dumps(value)
            
            if ttl is not None:
                return bool(await self._redis.set(key, value, ex=ttl, nx=True))
            else:
                return bool(await self._redis.set(key, value, nx=True))
        except RedisError as e:
            logger.error(f"Redis setnx error for key '{key}': {str(e)}")
            return False

# 全局Redis客户端实例
redis_client = RedisClient()
